/**
 * Copyright (c) 2015-2017, Henry Yang 杨勇 (gismail@foxmail.com).
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.lambkit.db;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.jfinal.plugin.IPlugin;
import com.jfinal.plugin.activerecord.ActiveRecordPlugin;
import com.jfinal.plugin.activerecord.Db;
import com.jfinal.plugin.activerecord.DbKit;
import com.jfinal.plugin.activerecord.DbPro;
import com.lambkit.db.datasource.DataSourceConfig;
import com.lambkit.db.datasource.DataSourceConfigManager;
import com.lambkit.plugin.activerecord.dialect.LambkitDialect;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 数据库 管理
 */
public class DataSourceManager {
    private static DataSourceManager manager = new DataSourceManager();

    private Map<String, LambkitDataSource> dataSources = null;

    public static DataSourceManager me() {
        return manager;
    }

    /**
     * 加入数据源
     * @param configName 数据源名称
     * @param dbType 数据库类型
     * @param dataSource 数据源
     * @return
     */
    public LambkitDataSource addDataSource(String configName, String dbType, DataSource dataSource) {
        ActiveRecordPlugin arp = new ActiveRecordPlugin(configName, dataSource);
        DefaultDataSourcePlugin dsPlugin = new DefaultDataSourcePlugin(arp);
        DataSourceConfig datasourceConfig = new DataSourceConfig();
        datasourceConfig.setName(configName);
        datasourceConfig.setType(dbType);
        LambkitDataSource lds = new LambkitDataSource(datasourceConfig, dsPlugin);
        getDataSources().put(lds.getConfigName(), lds);
        return lds;
    }
    /**
     * 加入数据源
     * @param datasourceConfig 数据库配置
     * @param dataSource
     */
    public LambkitDataSource addDataSource(DataSourceConfig datasourceConfig, DataSource dataSource) {
        ActiveRecordPlugin arp = new ActiveRecordPlugin(datasourceConfig.getName(), dataSource);
        DefaultDataSourcePlugin dsPlugin = new DefaultDataSourcePlugin(arp);
        LambkitDataSource lds = new LambkitDataSource(datasourceConfig, dsPlugin);
        getDataSources().put(lds.getConfigName(), lds);
        return lds;
    }

    /**
     * 按照数据库配置创建数据源，并加入
     * @param datasourceConfig 数据库配置
     * @return
     */
    public LambkitDataSource addDataSource(DataSourceConfig datasourceConfig) {
        if (datasourceConfig.isConfigOk()) {
            if(StrUtil.isBlank(datasourceConfig.getName()) && getDataSources().isEmpty()) {
                datasourceConfig.setName(DbKit.MAIN_CONFIG_NAME);
            }
            LambkitDataSource dataSource = new LambkitDataSource(datasourceConfig);
            getDataSources().put(datasourceConfig.getName(), dataSource);
            return dataSource;
        }
        return null;
    }

    public void addDataSource(LambkitDataSource datasource) {
        if (datasource != null) {
            getDataSources().put(datasource.getConfigName(), datasource);
        }
    }

    /**
     * 启动某个数据库
     * @param configName
     * @return
     */
    public LambkitDataSource start(String configName) {
        if(StrUtil.isBlank(configName)) {
            return null;
        }
        LambkitDataSource dbw = getDataSource(configName);
        if(dbw!=null) {
            dbw.start();
        }
        return dbw;
    }

    /**
     * 停用某个数据库
     * @param configName
     * @return
     */
    public LambkitDataSource stop(String configName) {
        if(StrUtil.isBlank(configName)) {
            return null;
        }
        LambkitDataSource dbw = getDataSource(configName);
        if(dbw!=null) {
            dbw.stop();
        }
        return dbw;
    }
    
    /**
     * 监听动态数据源存活状态，并移除长时间停用的数据源
     * @return
     */
    public int monitorDynamicDataSourceActive() {
    	return 0;
    }
    
    /**
     * 数据源加入Table
     * @param configName
     * @param table
     */
    public void addTable(String configName, LambkitTableMapping table) {
    	LambkitDataSource db = getDataSources().get(configName);
    	if(db!=null) {
    		db.addTable(table);
    	}
    }
    
    public LambkitDataSource getDataSource(String configName) {
    	return getDataSources().get(configName);
    }
    
    public LambkitDataSource removeDataSource(String configName) {
    	return getDataSources().remove(configName);
    }

    public Map<String, LambkitDataSource> initConifg(List<IPlugin> plugin) {
        DataSourceConfigManager.me().initConifg();
        // 所有的数据源
        Map<String, DataSourceConfig> datasourceConfigs = DataSourceConfigManager.me().getDatasourceConfigs();
        // 分库的数据源，一个数据源包含了多个数据源。
        Map<String, DataSourceConfig> shardingDatasourceConfigs = DataSourceConfigManager.me().getShardingDatasourceConfigs();
        if (shardingDatasourceConfigs != null && shardingDatasourceConfigs.size() > 0) {
            for (Map.Entry<String, DataSourceConfig> entry : shardingDatasourceConfigs.entrySet()) {
                String databaseConfig = entry.getValue().getShardingDatabase();
                if (StrUtil.isBlank(databaseConfig)) {
                    continue;
                }
                List<String> databases = StrUtil.split(databaseConfig, ",");
                for (String database : databases) {
                    DataSourceConfig datasourceConfig = datasourceConfigs.remove(database);
                    if (datasourceConfig == null) {
                        throw new NullPointerException("has no datasource config named " + database + ",plase check your sharding database config");
                    }
                    entry.getValue().addChildDatasourceConfig(datasourceConfig);
                }
            }
        }
        //所有数据源，包含了分库的和未分库的
        Map<String, DataSourceConfig> allDatasourceConfigs = new HashMap<>();
        if (datasourceConfigs != null) {
            allDatasourceConfigs.putAll(datasourceConfigs);
        }
        if (shardingDatasourceConfigs != null) {
            allDatasourceConfigs.putAll(shardingDatasourceConfigs);
        }
        for (Map.Entry<String, DataSourceConfig> entry : allDatasourceConfigs.entrySet()) {
            DataSourceConfig datasourceConfig = entry.getValue();
            LambkitDataSource db = addDataSource(datasourceConfig);
            db.setDynamicDataSource(false);

            if (db!=null) {
                if(db.getDataSourceInitPlugin()!=null) {
                    plugin.add(db.getDataSourceInitPlugin());
                }
            	plugin.add(db.getDataSourcePlugin());
            }
        }
        return getDataSources();
    }


	public Map<String, LambkitDataSource> getDataSources() {
		if(dataSources==null) {
			dataSources = MapUtil.newConcurrentHashMap();
		}
		return dataSources;
	}
	
	
//	public DbPro db(String configName) {
//		LambkitDataSource ds = getDataSource(configName);
//		return ds!=null ? ds.db() : null;
//	}
	 
	public DbPro db(String datasourceConfigName) {
		if(StrUtil.isBlank(datasourceConfigName)) {
			return Db.use();
		} else {
			return Db.use(datasourceConfigName);
		}
	}
	
	public LambkitDialect getDialect(String datasourceConfigName) {
		DbPro dp = db(datasourceConfigName);
		if(dp!=null) {
			return (LambkitDialect) dp.getConfig().getDialect();
		} else {
			return null;
		}
	}
}
